// Copyright 2015 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "src/profiler/strings-storage.h"

#include <memory>

#include "src/allocation.h"
#include "src/objects-inl.h"

namespace v8 {
namespace internal {

    bool StringsStorage::StringsMatch(void* key1, void* key2)
    {
        return strcmp(reinterpret_cast<char*>(key1), reinterpret_cast<char*>(key2)) == 0;
    }

    StringsStorage::StringsStorage()
        : names_(StringsMatch)
    {
    }

    StringsStorage::~StringsStorage()
    {
        for (base::HashMap::Entry* p = names_.Start(); p != nullptr;
             p = names_.Next(p)) {
            DeleteArray(reinterpret_cast<const char*>(p->value));
        }
    }

    const char* StringsStorage::GetCopy(const char* src)
    {
        int len = static_cast<int>(strlen(src));
        base::HashMap::Entry* entry = GetEntry(src, len);
        if (entry->value == nullptr) {
            Vector<char> dst = Vector<char>::New(len + 1);
            StrNCpy(dst, src, len);
            dst[len] = '\0';
            entry->key = dst.start();
            entry->value = entry->key;
        }
        return reinterpret_cast<const char*>(entry->value);
    }

    const char* StringsStorage::GetFormatted(const char* format, ...)
    {
        va_list args;
        va_start(args, format);
        const char* result = GetVFormatted(format, args);
        va_end(args);
        return result;
    }

    const char* StringsStorage::AddOrDisposeString(char* str, int len)
    {
        base::HashMap::Entry* entry = GetEntry(str, len);
        if (entry->value == nullptr) {
            // New entry added.
            entry->key = str;
            entry->value = str;
        } else {
            DeleteArray(str);
        }
        return reinterpret_cast<const char*>(entry->value);
    }

    const char* StringsStorage::GetVFormatted(const char* format, va_list args)
    {
        Vector<char> str = Vector<char>::New(1024);
        int len = VSNPrintF(str, format, args);
        if (len == -1) {
            DeleteArray(str.start());
            return GetCopy(format);
        }
        return AddOrDisposeString(str.start(), len);
    }

    const char* StringsStorage::GetName(Name name)
    {
        if (name->IsString()) {
            String str = String::cast(name);
            int length = Min(FLAG_heap_snapshot_string_limit, str->length());
            int actual_length = 0;
            std::unique_ptr<char[]> data = str->ToCString(
                DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL, 0, length, &actual_length);
            return AddOrDisposeString(data.release(), actual_length);
        } else if (name->IsSymbol()) {
            return "<symbol>";
        }
        return "";
    }

    const char* StringsStorage::GetName(int index)
    {
        return GetFormatted("%d", index);
    }

    const char* StringsStorage::GetConsName(const char* prefix, Name name)
    {
        if (name->IsString()) {
            String str = String::cast(name);
            int length = Min(FLAG_heap_snapshot_string_limit, str->length());
            int actual_length = 0;
            std::unique_ptr<char[]> data = str->ToCString(
                DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL, 0, length, &actual_length);

            int cons_length = actual_length + static_cast<int>(strlen(prefix)) + 1;
            char* cons_result = NewArray<char>(cons_length);
            snprintf(cons_result, cons_length, "%s%s", prefix, data.get());

            return AddOrDisposeString(cons_result, cons_length);
        } else if (name->IsSymbol()) {
            return "<symbol>";
        }
        return "";
    }

    base::HashMap::Entry* StringsStorage::GetEntry(const char* str, int len)
    {
        uint32_t hash = StringHasher::HashSequentialString(str, len, kZeroHashSeed);
        return names_.LookupOrInsert(const_cast<char*>(str), hash);
    }

} // namespace internal
} // namespace v8
